* Optimize performance of DateTime.
Prefer Qt::UTC TimeSpec for storing creation_time.
This avoids very significant performance hits from converting
to/from Qt::LocalTime, which involve DST calculations as well
as the usual offsets.
Test suite with this commit runs at 63% of wall-clock time of the
baseline (time ./testo)!
* remove obsolete fromTime_t and use Qt::UTC.
* Delete obsolete operators and methods of DateTime.
* document motivation for Qt::UTC as default timespec.
Modify Waypoint::SetCreationTime so it can be used with seconds, milliseconds,
or both operands. This method is implemented without resetting the
Qt::TimeSpec. It will be efficient if the existing TimeSpec is
Qt::UTC as set up by gpsbabel::DateTime default constructor.
QString CreationTimeXML() const;
gpsbabel::DateTime GetCreationTime() const;
void SetCreationTime(const gpsbabel::DateTime& t);
- void SetCreationTime(time_t t);
- void SetCreationTime(time_t t, int ms);
+ void SetCreationTime(qint64 t, qint64 ms = 0);
geocache_data* AllocGCData();
int EmptyGCData() const;
};
if (alt != 0xffff) {
waypt->altitude = (alt / 5.0) - 500;
}
- waypt->SetCreationTime(QDateTime::fromTime_t(GPS_Math_Gtime_To_Utime(timestamp)));
+ waypt->SetCreationTime(GPS_Math_Gtime_To_Utime(timestamp));
if (speed != 0xffff) {
WAYPT_SET(waypt, speed, speed / 1000.0f);
}
route_head* route = route_head_alloc();
- QDateTime qtx;
- qtx.setTimeSpec(Qt::UTC);
- qtx.setTime_t(tx);
+ QDateTime qtx = QDateTime::fromSecsSinceEpoch(tx, Qt::UTC);
route->rte_name = "Tracklog ";
route->rte_name += qtx.toString(Qt::ISODate);
route_add_head(route);
if (!strptime(c, "%Y%m%d", &tm2)) {
fatal("Bad date '%s'.\n", c);
}
- wpt->creation_time += mkgmtime(&tm2);
+ wpt->creation_time = wpt->creation_time.addSecs(mkgmtime(&tm2));
break;
case 10: // Unknown. Ignored.
case 11: // Bearing. Ignored.
static void
gopal_write_waypt(const Waypoint* wpt)
{
- char tbuffer[64];
int fix=fix_unknown;
//TICK; TIME; LONG; LAT; HEIGHT; SPEED; UN; HDOP; SAT
//3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
- snprintf(tbuffer, sizeof(tbuffer), "%06d", wpt->creation_time.hms());
+ QString tbuffer = wpt->creation_time.toString("HHmmss");
if (wpt->fix!=fix_unknown) {
switch (wpt->fix) {
case fix_none:
}
//MSVC handles time_t as int64, gcc and mac only int32, so convert it:
unsigned long timestamp = (unsigned long)wpt->GetCreationTime().toTime_t();
- gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n",timestamp,tbuffer, wpt->longitude, wpt->latitude,wpt->altitude,
+ gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n",timestamp, CSTR(tbuffer), wpt->longitude, wpt->latitude,wpt->altitude,
wpt->speed,fix,wpt->hdop,wpt->sat);
}
fread_discard(file_in, 1);
wpt->SetCreationTime(fread_long(file_in));
if (wpt->creation_time.isValid()) {
- wpt->creation_time += EPOCH89DIFF;
+ wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
}
fread_discard(file_in, 2);
wpt->altitude = fread_single(file_in);
convert_datum(&wpt->latitude, &wpt->longitude);
wpt->SetCreationTime(fread_long(file_in));
if (wpt->creation_time.isValid()) {
- wpt->creation_time += EPOCH89DIFF;
+ wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
}
start_new = fread_byte(file_in);
wpt->altitude = fread_single(file_in);
gbfgetc(file_in);
/* POSIX timestamp (a.k.a. UNIX Epoch) - seconds since Jan 1, 1970 */
- wpt_tmp->SetCreationTime(QDateTime::fromTime_t(gbfgetint32(file_in)));
+ wpt_tmp->SetCreationTime(gbfgetint32(file_in));
/* Long/Lat */
wpt_tmp->longitude = gbfgetdbl(file_in) / DEGREESTORADIANS; /* rad to deg */
route_head* head;
Waypoint* prev = nullptr;
- QDateTime time = current_time();
+ QDateTime time = current_time().toUTC();
int points = (opt_points) ? atoi(opt_points) : rand_int(128) + 1;
if (doing_trks || doing_rtes) {
if (opt_points) {
realtime->points = atoi(opt_points);
}
- realtime->time = current_time();
+ realtime->time = current_time().toUTC();
}
void
public:
// As a crutch, mimic the old behaviour of an uninitialized creation time
// being 1/1/1970.
- DateTime() {
- setTime_t(0);
+ // The choice of Qt::TimeSpec here can be critical to performance.
+ // With a Qt::LocalTime conversions between Qt::UTC and Qt::LocalTime
+ // can be required when using a method such as *Epoch, add*.
+ // Using Qt::Utc avoids these conversions.
+ // The lowranceusr regression test was measured taking 2.7x longer,
+ // and the entire regression suite took 1.7x longer, with
+ // Qt::LocalTime compared to Qt::UTC on ubuntu bionic.
+ // Note that these conversions can be required if the Qt::TimeSpec is
+ // set to Qt:LocalTime after construction.
+ DateTime() : QDateTime(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)) {
}
DateTime(const QDate& date, const QTime& time) : QDateTime(date, time) {}
DateTime(const QDateTime& dt) : QDateTime(dt) {}
- // TODO: this should go away in favor of .addSecs().
- // add time_t without losing any existing milliseconds.
- DateTime& operator+=(const time_t& t) {
- QDateTime dt = addSecs(t);
- setDate(dt.date());
- setTime(dt.time());
- return *this;
- }
-
- // Integer form: YYMMDD
- int ymd() const {
- QDate d(date());
- return d.year() * 10000 + d.month() * 100 + d.day();
- }
-
- int ddmmyy() const {
- QDate d(date());
- return d.day() * 10000 + d.month() * 100 + d.year();
- }
-
- int hms() const {
- QTime t(time());
- return t.hour() * 10000 + t.minute() * 100 + t.second();
- }
-
// Temporary: Override the standard, also handle time_t 0 as invalid.
bool isValid() const {
return date().isValid() && time().isValid() && toTime_t() > 0;
struct tm tm = *gmtime(&res);
res = mklocaltime(&tm);
}
- return QDateTime::fromTime_t(res);
+ return QDateTime::fromSecsSinceEpoch(res, Qt::UTC);
}
static bool
}
if (opt_utc) {
- wpt->creation_time += atoi(opt_utc) * SECONDS_PER_HOUR;
+ wpt->creation_time = wpt->creation_time.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
}
}
// We might wrap to a different day by overriding the TZ offset.
dt = dt.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
} else {
- dt = wpt->GetCreationTime();
+ dt = wpt->GetCreationTime().toLocalTime();
}
QString date = dt.toString("yyyy/MM/dd");
*fout << unicsv_fieldsep << date;
t = wpt->GetCreationTime().toUTC().time();
t = t.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
} else {
- t = wpt->GetCreationTime().time();
+ t = wpt->GetCreationTime().toLocalTime().time();
}
QString out;
if (t.msec() > 0) {
current_time()
{
if (gpsbabel_testmode()) {
- return QDateTime::fromTime_t(0);
+ return QDateTime::fromMSecsSinceEpoch(0, Qt::UTC);
}
- return QDateTime::currentDateTime();
+ return QDateTime::currentDateTimeUtc();
}
/*
}
void
-Waypoint::SetCreationTime(time_t t)
+Waypoint::SetCreationTime(qint64 t, qint64 ms)
{
- creation_time = QDateTime::fromTime_t(t);
-}
-
-void
-Waypoint::SetCreationTime(time_t t, int ms)
-{
- creation_time.setTime_t(t);
- creation_time = creation_time.addMSecs(ms);
+ creation_time.setMSecsSinceEpoch((t * 1000) + ms);
}
geocache_data*
case XT_TIMET_TIME_MS: {
/* Time as time_t in milliseconds */
bool ok;
- wpt->SetCreationTime(QDateTime::fromMSecsSinceEpoch(value.toLongLong(&ok)));
+ wpt->SetCreationTime(0, value.toLongLong(&ok));
if (!ok) {
warning("parse of string '%s' on line number %d as TIMET_TIME_MS failed.\n", s, line_no);
}
break;
case XT_LOCAL_TIME:
if (!gpsbabel_testmode()) {
- wpt->creation_time += sscanftime(s, fmp.printfc.constData(), 0);
+ wpt->creation_time = wpt->creation_time.addSecs(sscanftime(s, fmp.printfc.constData(), 0));
} else {
/* Force constant time zone for test */
- wpt->creation_time += sscanftime(s, fmp.printfc.constData(), 1);
+ wpt->creation_time = wpt->creation_time.addSecs(sscanftime(s, fmp.printfc.constData(), 1));
}
break;
/* Useful when time and date are in separate fields
GMT / Local offset is handled by the two cases above */
case XT_HMSG_TIME:
case XT_HMSL_TIME:
- wpt->creation_time += addhms(s, fmp.printfc.constData());
+ wpt->creation_time = wpt->creation_time.addSecs(addhms(s, fmp.printfc.constData()));
break;
case XT_ISO_TIME:
case XT_ISO_TIME_MS: